package panda.client.listeners;

import java.util.List;

import panda.client.Cardpanda;
import panda.client.ui.widgets.Bin;
import panda.client.ui.widgets.Counter;
import panda.client.ui.widgets.NoteTip;
import panda.client.ui.widgets.Tooltip;

import com.google.gwt.event.dom.client.MouseDownEvent;
import com.google.gwt.event.dom.client.MouseDownHandler;
import com.google.gwt.event.dom.client.MouseMoveEvent;
import com.google.gwt.event.dom.client.MouseMoveHandler;
import com.google.gwt.event.dom.client.MouseOutEvent;
import com.google.gwt.event.dom.client.MouseOutHandler;
import com.google.gwt.event.dom.client.MouseOverEvent;
import com.google.gwt.event.dom.client.MouseOverHandler;
import com.google.gwt.event.dom.client.MouseUpEvent;
import com.google.gwt.event.dom.client.MouseUpHandler;
import com.google.gwt.user.client.Command;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.DeferredCommand;
import com.google.gwt.user.client.ui.Widget;

public class CounterDragHandler implements MouseMoveHandler, MouseOverHandler, MouseDownHandler, MouseUpHandler,
		MouseOutHandler {
	private int xOffset; // position in source widget where mouse is before dragging
	private int yOffset;
	private boolean onMouseDownTriggeredHere = false; /* prevents rogue onMouseUp events without firing omd first */

	/**
	 * setBinHover if this card is dragged over a bin
	 */
	private void setBinHover(Counter counter, int x, int y) {
		final List<Bin> bins = Cardpanda.clientModel.getBinsThisClientControls();
		for(Bin bin : bins) {
			if(withinBinCoordinates(counter, bin, x, y)) {
				bin.setHover();
			} else {
				bin.removeHover();
			}
		}
	}

	/**
	 * Returns true if the mouse is positioned over target bin.
	 */
	private boolean withinBinCoordinates(Counter counter, Bin bin, int x, int y) {
		/* Bounds = absolute coordinates of mouse cursor */
		int xBound = counter.getAbsoluteLeft() - Cardpanda.canvas.getAbsoluteLeft() + x;
		int yBound = counter.getAbsoluteTop() - Cardpanda.canvas.getAbsoluteTop() + y;

		/* The bin's 4 corners lie on xLeft xRight yTop yBottom. */
		/* Note: these absolute positions rely on the canvas's absolute left and top as well. */
		int xLeft = bin.getLabel().getAbsoluteLeft() - Cardpanda.canvas.getAbsoluteLeft();
		int yTop = bin.getLabel().getAbsoluteTop() - Cardpanda.canvas.getAbsoluteTop();
		int yBottom = bin.getLabel().getAbsoluteTop() + bin.getHeight() - Cardpanda.canvas.getAbsoluteTop();
		int xRight = bin.getLabel().getAbsoluteLeft() + bin.getWidth() - Cardpanda.canvas.getAbsoluteLeft();

		return xBound >= xLeft && xBound <= xRight && yBound >= yTop && yBound <= yBottom;
	}

	public void onMouseMove(MouseMoveEvent event) {
		int x = event.getX();
		int y = event.getY();
		Widget source = (Widget) event.getSource();

		DOM.eventPreventDefault(DOM.eventGetCurrentEvent());
		Counter counter = (Counter) source;
		int xAbs = x + source.getAbsoluteLeft() - Cardpanda.canvas.getAbsoluteLeft();
		int yAbs = y + source.getAbsoluteTop() - Cardpanda.canvas.getAbsoluteTop();

		if(counter.isDragging()) {
			setBinHover(counter, x, y);
			Cardpanda.canvas.setWidgetPosition(source, xAbs - xOffset, yAbs - yOffset);
		}
	}

	public void onMouseOver(MouseOverEvent event) {
		Widget source = (Widget) event.getSource();

		source.addStyleName("mouseStyle-mouseover");
		Cardpanda.focusPnl.setFocus(true);
	}

	public void onMouseDown(MouseDownEvent event) {
		int x = event.getX();
		int y = event.getY();
		Widget source = (Widget) event.getSource();
		Counter target = (Counter) source;
		onMouseDownTriggeredHere = true;

		/* code to remove a counter via ctrl-click */
		if(event.isControlKeyDown()) {
			removeCounter((Counter) event.getSource());
			
			return;
		} else {
			/* if it's not a control click, initiate dragging of the counter */
			
			DOM.eventPreventDefault(DOM.eventGetCurrentEvent());
			target.addStyleName("mouseStyle-dragging");

			DOM.setCapture(target.getElement());
			target.setDragging(true);
			xOffset = x;
			yOffset = y;
		}
	}

	/**
	 * Remove the drag style.<br>
	 * If this counter is over a bin, remove it from the game<br>
	 * Update the server.
	 */
	public void onMouseUp(MouseUpEvent event) {
		int x = event.getX();
		int y = event.getY();
		Widget source = (Widget) event.getSource();
		Counter counter = (Counter) source;

		/* prevents rogue onMouseUps from triggering a move when the this.onMouseDown did not trigger. */
		/* specifically, prevents a null move when counters are created though mouse shortcut. */
		if(!onMouseDownTriggeredHere) {
			return;
		}
		
		source.removeStyleName("mouseStyle-dragging");
		DOM.releaseCapture(source.getElement());
		counter.setDragging(false);

		List<Bin> bins = Cardpanda.clientModel.getBinsThisClientControls();
		Bin targetBin = null;
		int numBinsUnderCounter = 0; /* number of bins this card is hovering over */
		boolean binUnderCounterFound = false;

		for(Bin bin : bins) {
			if(withinBinCoordinates(counter, bin, x, y)) {
				numBinsUnderCounter++;
				binUnderCounterFound = true;
				targetBin = bin;
			}
		}

		assert numBinsUnderCounter < 2 : "There shouldn't be more than 1 bin this counter is hovering over";

		if(binUnderCounterFound) {
			removeCounter(counter, targetBin);
		} else {
			int counterx = source.getAbsoluteLeft() - Cardpanda.canvas.getAbsoluteLeft();
			int countery = source.getAbsoluteTop() - Cardpanda.canvas.getAbsoluteTop();
			String logForMe = "You move a counter to " + "(" + counterx + ", " + countery + ").";
			String logForOthers = Cardpanda.clientModel.getSessionID().getName() + " moves a counter to " + "(" + counterx
					+ ", " + countery + ").";
			NoteTip.get().log(logForMe, logForOthers);
		}

		onMouseDownTriggeredHere = false; /* reset */
		Cardpanda.sendModelUpdateToServer(Cardpanda.clientModel.getSessionID());
	}
	
	/* removes a counter and removes the hover style from the bin the counter was dragged on top of to be removed */
	private void removeCounter(Counter counter, Bin targetBin) {
		removeCounter(counter);
		
		targetBin.removeHover();
		
	}
	
	/* encapsulated method to remove a counter from the canvas */
	private void removeCounter(Counter counter) {
		String logForMe = "You remove a counter from the game. "/* + bin.getOwnerName() + bin.getName() */;
		String logForOthers = Cardpanda.clientModel.getSessionID().getName() + " removes a counter from the game.";
		NoteTip.get().log(logForMe, logForOthers);
		Cardpanda.clientModel.removeCounter(counter);
		
		DeferredCommand.addCommand(new Command() {
			public void execute() {
				Tooltip.get().hide(); /* counters the showTooltip method in tooltipListener's onmouseup */
			}
		});
	}

	public void onMouseOut(MouseOutEvent event) {
		Widget source = (Widget) event.getSource();
		source.removeStyleName("mouseStyle-mouseover");
	}
}